﻿/*
SINACOR - BMF&Bovespa
Arquivo: Exportacao.cs
Criado em: 28-jun-2010 08:28:55
Arquiteto Responsável: p-wgeneroso
*/
using System;
using System.Collections.Generic;
using System.Text;
using Sinacor.Infra.Core.ProcessManager.Server;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using PSE.ProcessManager.Common.Process;
using PSE.Framework.Communication.Client.File.FlatFile;
using PSE.Framework.Communication.Configuration.File.FlatFile;
using PSE.Framework.Communication.Configuration;
using System.IO;
using PSE.Framework.Communication.Adapters.File;
using Sinacor.Infra.Common.Validation;
using Sinacor.Infra.Service.Validation;
using System.Configuration;
using System.Linq;

namespace Sinacor.Process.Bovespa.Alocacao.Server.ProcessLogic 
{
	public class Exportacao : BaseProcessLogic 
	{
		#region Atributos

		private int _erroCodigoBrokerNaoInformado = 109786;
		private int _erroFalhaInconsistencia = 109789;
		private int _erroFormadorMercado = 109788;
		private int _erroInconsistenciasExportacao = 109787;
        private int _erroBrokerNaoCadastrado = 109831;
        private int _erroBrokerInativo = 109832;
        private int _erroBrokerBloqueado = 109833;
        private int _erroExportacaoSemRegistros = 109840;

		#endregion

		#region Eventos

        public event EventHandler<ProcessEventArgs<ProcessEntity.ExportacaoResultado>> ExportacaoConcluida;
		#endregion

		#region Propriedades

		public int ErroCodigoBrokerNaoInformado
		{
			get { return _erroCodigoBrokerNaoInformado; }
			set { _erroCodigoBrokerNaoInformado = value; }
		}

		public int ErroFalhaInconsistencia
		{
			get { return _erroFalhaInconsistencia; }
			set { _erroFalhaInconsistencia = value; }
		}

		public int ErroFormadorMercado
		{
			get { return _erroFormadorMercado; }
			set { _erroFormadorMercado = value; }
		}

		public int ErroInconsistenciasExportacao
		{
			get { return _erroInconsistenciasExportacao; }
			set { _erroInconsistenciasExportacao = value; }
		}

        public int ErroBrokerNaoCadastrado
        {
            get { return _erroBrokerNaoCadastrado; }
            set { _erroBrokerNaoCadastrado = value; }
        }

        public int ErroBrokerInativo
        {
            get { return _erroBrokerInativo; }
            set { _erroBrokerInativo = value; }
        }

        public int ErroBrokerBloqueado
        {
            get { return _erroBrokerBloqueado; }
            set { _erroBrokerBloqueado = value; }
        }

        public int ErroExportacaoSemRegistros
        {
            get { return _erroExportacaoSemRegistros; }
            set { _erroExportacaoSemRegistros = value; }
        }

		#endregion

		#region Métodos
		/// 
		/// <param name="entity"></param>
		public void ExportarAlocacao(ProcessEntity.Parameter.FiltroExportacaoAlocacao entity)
		{
            ResourceAccess.Exportacao raExportacao = new ResourceAccess.Exportacao();
            ProcessEntity.Resultado resultado = null;
            int codigoEmpresaBolsa = 0;
            decimal porcentagemPorRegistro = 0;
            Translator.ProcessEntityToLayout.Alocacao toLayoutAlocacao = new Translator.ProcessEntityToLayout.Alocacao();
            List<Layout.AlocacaoDetail> alocacaoDetails = null;
            Layout.AlocacaoHeader header = null;
            Layout.AlocacaoTrailer trailer = null;
            int registroAtual = 0;
            List<List<Layout.AlocacaoDetail>> listaArquivos = null;
            int contador = 0;
            FileInfo fi = null;
            string arquivo = string.Empty;
            string caminho = string.Empty;
            ProcessEntity.ExportacaoResultado resultadoExportacao = new ProcessEntity.ExportacaoResultado();
            ProcessEntity.Cliente broker = null;
            Communication.Cliente communicationCliente;

            ResourceAccess.ImportacaoExportacao raNegImportacaoExportacao = new ResourceAccess.ImportacaoExportacao();

            //Valida Broker
            if ((entity.EspecificacaoBroker || entity.ClientesBroker) && !entity.CodigoBroker.HasValue)
                throw new BusinessException(this.ErroCodigoBrokerNaoInformado);

            SinacorValidation validationRuleSet = new SinacorValidation();
            validationRuleSet.AddValidation<ProcessEntity.Parameter.FiltroExportacaoAlocacao>(entity);
            validationRuleSet.Validate();

            communicationCliente = new Communication.Cliente();

            ValidationException validationBroker = new ValidationException();

            //Validar Cliente Broker
            if (entity.CodigoBroker.HasValue && entity.CodigoBroker.Value > 0)
            {
                broker = communicationCliente.ObterCliente(new ProcessEntity.Parameter.FiltroCliente { CodigoCliente = entity.CodigoBroker });

                if (broker == null)
                    throw new BusinessException(this.ErroBrokerNaoCadastrado);
            }

            ValidationException validation = new ValidationException();

            //Valida Formador de Mercado
            if (raExportacao.VerificarFormadorMercado(entity))
                validation.AddError(this.ErroFormadorMercado);

            //Consisti especificacao broker
            resultado = raExportacao.ConsistirEspecificacaoBroker(entity);

            if (resultado != null)
            {
                if (!resultado.Sucesso)
                {
                    throw new BusinessException(this.ErroFalhaInconsistencia);
                }
                else
                {
                    //Verifica erros
                    if (raExportacao.VerificarErrosConsistencia(entity))
                        validation.AddError(this.ErroInconsistenciasExportacao);
                }
            }

            if (validation.ValidationErrors.Count > 0)
                this.HandleException(validation, false);

            //Obtem codigo empresa bolsa
            codigoEmpresaBolsa = raNegImportacaoExportacao.ObterCodigoEmpresaBolsa();

            //Consulta exportacao alocacao
            alocacaoDetails =
                toLayoutAlocacao.ToAlocacaoDetail(raExportacao.ConsultarExportacaoAlocacao(entity, codigoEmpresaBolsa));

            if (alocacaoDetails.Count != 0)
                porcentagemPorRegistro = 100M / (decimal)alocacaoDetails.Count;
            else
                throw new BusinessException(this.ErroExportacaoSemRegistros);

            FlatFileOutputConfiguration _config = CommunicationConfigurationManager.GetInstance().GetCommunicationClientConfiguration("ExportacaoIESC") as FlatFileOutputConfiguration;

            //Monta header
            header = new Sinacor.Process.Bovespa.Alocacao.Server.Layout.AlocacaoHeader
            {
                CodigoDestino = codigoEmpresaBolsa,
                CodigoOrigem = "CBLC",
                CodigoUsuario = codigoEmpresaBolsa,
                DataPregao = entity.DataPregao,
                DataGeracaoArquivo = DateTime.Now,
                Reserva = string.Empty,
                Reserva2 = string.Empty
            };

            //Monta trailer
            trailer = new Sinacor.Process.Bovespa.Alocacao.Server.Layout.AlocacaoTrailer
            {
                CodigoDestino = codigoEmpresaBolsa,
                CodigoOrigem = "CBLC",
                CodigoUsuario = codigoEmpresaBolsa,
                DataGeracaoArquivo = DateTime.Now,
                TotalRegistros = alocacaoDetails.Count + 2,
                Reserva = string.Empty,
                Reserva2 = string.Empty
            };

            //Dividi os arquivos respeitando o número máximo de registros
            listaArquivos = this.DividirArquivo(alocacaoDetails);

            fi = new FileInfo(entity.NomeArquivo);
            resultadoExportacao.Arquivos = new List<String>();

            using (FlatFileOutputClient client = new FlatFileOutputClient())
            {
                //Abri arquivo
                client.Open("ExportacaoIESC");

                //Percorre a lista de arquivos
                foreach (List<Layout.AlocacaoDetail> itemLista in listaArquivos)
                {
                    try
                    {
                        if (IsAborted)
                            break;
                        
                        caminho = _config == null ? null : _config.OutputPath;

                        if (fi.Name.IndexOf(".") < 0)
                            arquivo = fi.Name.Insert(fi.Name.Length, string.Concat("_", contador.ToString("00")));
                        else
                            arquivo = fi.Name.Insert(fi.Name.IndexOf("."), string.Concat("_", contador.ToString("00")));

                        //Adiciona caminho do arquivo para o retorno da exportação
                        resultadoExportacao.Arquivos.Add(Path.Combine(fi.Directory.FullName, arquivo));

                        //Abre arquivo
                        client.OpenFile(new FileOpenParameter(caminho, arquivo));

                        //Atribui total de registros incluindo header e trailer
                        trailer.TotalRegistros = itemLista.Count + 2;

                        //Escreve header no arquivo
                        client.WriteRecord(header, "AlocacaoHeader");

                        //Percorre a lista de registro por arquivo
                        foreach (Layout.AlocacaoDetail item in itemLista)
                        {
                            if (IsAborted)
                                break;

                            //Escreve registro
                            client.WriteRecord(item, "AlocacaoDetail");

                            registroAtual++;

                            //Atualiza status
                            SendStatus(Convert.ToInt32(Math.Floor(porcentagemPorRegistro * registroAtual)), "Bovespa.ImportacaoExportacao.Message.ExportandoArquivo");
                        }

                        //Escreve trailer no arquivo
                        client.WriteRecord(trailer, "AlocacaoTrailer");

                        //Fechas Arquivo
                        client.CloseFile();
                    }
                    catch
                    {
                        if (client.Status == PSE.Framework.Communication.CommunicationStatus.Opened)
                            client.CancelFile();

                        throw;
                    }

                    contador++;
                }
            }

            if (!IsAborted)
            {
                //Grava log
                raExportacao.GravarLogExportacao(entity, alocacaoDetails.Count);

                //Conclui exportação
                if (ExportacaoConcluida != null)
                    ExportacaoConcluida(this, new ProcessEventArgs<Sinacor.Process.Bovespa.Alocacao.ProcessEntity.ExportacaoResultado>(resultadoExportacao));
            }
		}

        private List<List<Layout.AlocacaoDetail>> DividirArquivo(List<Layout.AlocacaoDetail> alocacoes)
        {
            List<List<Layout.AlocacaoDetail>> listaArquivo = new List<List<Layout.AlocacaoDetail>>();
            List<Layout.AlocacaoDetail> arquivo = new List<Layout.AlocacaoDetail>();
            int quantidadeRegistrosAlocacao = 0;
            int contador = 0;
            DateTime ultimaDataPregao = DateTime.MinValue;
            string ultimoCodigoNegociacao = "";
            int ultimoNegocio = 0;
            string ultimaNaturezaOperacao = "";

            //Verica se há registros para a exportação
            if (alocacoes == null || alocacoes.Count == 0)
            {
                //Adiciona arquivo vazio
                listaArquivo.Add(new List<Layout.AlocacaoDetail>());
            }
            else
            {
                //Recupera configuração da quantidade máxima de registros para a exportação
                Int32.TryParse(ConfigurationSettings.AppSettings["QuantidadeRegistrosAlocacao"], out quantidadeRegistrosAlocacao);

                //Percorre registros
                alocacoes.ForEach(
                    delegate(Layout.AlocacaoDetail item)
                    {
                        contador++;

                        if (contador > quantidadeRegistrosAlocacao && ultimaDataPregao != item.DataPregao && ultimoCodigoNegociacao != item.CodigoNegociacao && ultimoNegocio != item.NumeroNegocio && ultimaNaturezaOperacao != item.NaturezaOperacao)
                        {
                            listaArquivo.Add(arquivo);

                            arquivo = new List<Layout.AlocacaoDetail>();
                            contador = 0;
                        }

                        arquivo.Add(item);

                        ultimaDataPregao = item.DataPregao;
                        ultimoCodigoNegociacao = item.CodigoNegociacao;
                        ultimoNegocio = item.NumeroNegocio;
                        ultimaNaturezaOperacao = item.NaturezaOperacao;
                    });

                if (arquivo != null && arquivo.Count > 0)
                    listaArquivo.Add(arquivo);
            }

            return listaArquivo;
        }

		#endregion
	}

}